package nl.hanze.web.homegrownrpc.generic;

import com.sun.xml.internal.ws.message.saaj.SAAJHeader;
import com.sun.xml.internal.ws.util.StringUtils;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;

public class StubGenerator {
    private StringBuffer stbTemp;
    private String fullInterfaceName;
    private String nameStub;
    private String packageStub;
    private String locationStub;
    private HashMap<String, String> wrapperClass;

    public StubGenerator(String fullInterfaceName, String baseLocationStub) {
        this.fullInterfaceName=fullInterfaceName;
        this.nameStub=getInterfaceName()+"Stub";
        this.packageStub=getPackageName(false);
        this.locationStub=baseLocationStub+File.separatorChar+getPackageName(true);
        wrapperClass=new HashMap<String, String>();
        wrapperClass.put("boolean", "java.lang.Boolean");
        wrapperClass.put("byte", "java.lang.Byte");
        wrapperClass.put("short", "java.lang.Short");
        wrapperClass.put("int", "java.lang.Integer");
        wrapperClass.put("long", "java.lang.Long");
        wrapperClass.put("float", "java.lang.Float");
        wrapperClass.put("double", "java.lang.Double");
        wrapperClass.put("char", "java.lang.Char");
    }

    public void generateStub() throws Exception {
        stbTemp=new StringBuffer();
        generatePackageName();
        generateEmptyLine();
        generateClassLine();
        Method[] methods=searchMethods();
        for(Method method : methods) {
            generateEmptyLine();
            generateMethod(method);
        }
        generateEmptyLine();
        generateLastLine();
        saveGeneratedClass();
    }

    private void generatePackageName() {
        if (packageStub!=null && packageStub.length()!=0) {
            stbTemp.append("package " + packageStub + ";");
        }
    }

    private void generateClassLine() {
        stbTemp.append("public class ");
        stbTemp.append(nameStub);
        stbTemp.append(" extends nl.hanze.web.homegrownrpc.generic.Stub");
        stbTemp.append(" implements " + fullInterfaceName + " {");
    }

    private Method[] searchMethods() throws Exception {
        Class i = Class.forName(fullInterfaceName);
        return i.getDeclaredMethods();
    }

    private void generateMethod(Method method) {
        String returnType=method.getReturnType().getCanonicalName();
        boolean isPrimitive=wrapperClass.containsKey(returnType);
        boolean isVoid=returnType.equals("void");
        String methodName=method.getName();
        Class[] params=method.getParameterTypes();
        boolean hasParameters=params.length!=0;

        // Go through the arguments for this method once and generate
        // Strings that we need later
        String sigParams = "";  // The params as they appear in the signature
        String paramTypes = ""; // The parameter types seperated by commas
        String paramVars = "";  // The parameter names seperated by commas
        String sep = "";
        int n = 0;
        for (Class c: params) {
            String pName = getLastPoint(c.getName().toLowerCase()) + n;
            String pType = c.getName();
            sigParams  += sep + pType + " " + pName;
            paramTypes += sep + pType + ".class";
            paramVars  += sep + pName;
            sep = ", ";
        }
        
        // Start method signature:
        stbTemp.append("\tpublic ");
        stbTemp.append(returnType + " ");
        stbTemp.append(methodName + "(");
        stbTemp.append(sigParams);
        stbTemp.append(") throws Exception {");
        generateEmptyLine();
        // If the return type isn't a void, we need to return it.
        if (!isVoid){
            stbTemp.append("\t\treturn ");
            if (isPrimitive) {
                stbTemp.append("((");
                stbTemp.append(wrapperClass.get(returnType));
            } else {
                stbTemp.append("(");
                stbTemp.append(returnType);
            }
            stbTemp.append(") ");
        } else {
            stbTemp.append("\t\t");
        }
        // Here we generate code that actually invokes this method
        stbTemp.append("invokeSkel(\"");
        stbTemp.append(methodName);
        stbTemp.append("\", ");
        if (hasParameters) {
            stbTemp.append("new Class[] {");
            stbTemp.append(paramTypes);
            stbTemp.append("}, new Object[] {");
            stbTemp.append(paramVars);
            stbTemp.append("}");
        } else {
            stbTemp.append("null, null");
        }
        if (!isVoid && isPrimitive) {
            stbTemp.append(")).");
            stbTemp.append(returnType);
            stbTemp.append("Value();");
        } else {
            stbTemp.append(");");
        }
        stbTemp.append("\r\n\t}");
        generateEmptyLine();
    }

    private void generateEmptyLine() {
        stbTemp.append("\r\n");
    }

    private void generateLastLine() {
        stbTemp.append("}\r\n");
    }

    private void saveGeneratedClass() throws Exception {
        File file=new File(locationStub+"\\"+nameStub+".java");
        file.createNewFile();
        FileWriter fw = new FileWriter(file);
        BufferedWriter out = new BufferedWriter(fw);
        out.write(stbTemp.toString());
        out.close();// */
    }

    private String getInterfaceName() {
        return getLastPoint(fullInterfaceName);
    }
    
    private String getLastPoint(String from) {
        int i=from.lastIndexOf('.');
        if (i==-1) return from;
        i++;
        return from.substring(i);
    }

    private String getPackageName(boolean useSlashes) {
        int i=fullInterfaceName.lastIndexOf('.');
        if (i==-1) return "";
        if (useSlashes)
            return fullInterfaceName.substring(0,i).replace('.', File.separatorChar);
        else
            return fullInterfaceName.substring(0,i);
    }

    public static void main(String[] args) throws Exception {
        StubGenerator sg=new StubGenerator("nl.hanze.web.homegrownrpc.addressbook.AddressBook", "C:\\Temp");
        sg.generateStub();
    }

}
